using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace WindowsPhoneGame1.Shapes
{
    public class Polygon : Shape
    {
        private PointF[] Points { get; set; }
        public override RectangleF Bounds { get; protected set; }

        /// <summary>
        /// Constructs a Polygon. The Points must be provided
        /// in clockwise order (for simple Polygons).
        /// </summary>
        /// <param name="points"></param>
        public Polygon(PointF[] points) : base()
        {
            Points = points;
            SetBounds();
        }

        public override void SetBounds()
        {
            float minX = Points[0].X;
            float maxX = Points[0].X;
            float minY = Points[0].Y;
            float maxY = Points[0].Y;

            foreach (PointF p in Points)
            {
                if (p.X < minX)
                    minX = p.X;
                if (p.X > maxX)
                    maxX = p.X;
                if (p.Y < minY)
                    minY = p.Y;
                if (p.Y > maxY)
                    maxY = p.Y;
            }

            Bounds = new RectangleF(minX, minY, maxX - minX, maxY - minY);
        }

        public ContainmentType Contains(PointF other)
        {
            if (Bounds.Contains(other) == ContainmentType.Disjoint)
                return ContainmentType.Disjoint;

            Line ray = new Line(
                new PointF(Bounds.X - Bounds.Width * 0.01f, other.Y),
                other);

            bool inside = false;

            for (int i = 0; i < Points.Length; i++)
            {
                Line edge;
                if (i == 0)
                    edge = new Line(Points[Points.Length - 1], Points[0]);
                else
                    edge = new Line(Points[i - 1], Points[i]);

                if (ray.Contains(edge) == ContainmentType.Intersects)
                    inside = !inside;
            }

            if (inside)
                return ContainmentType.Contains;
            return ContainmentType.Disjoint;
        }

        public ContainmentType Contains(Line other)
        {
            if (Bounds.Contains(other.Bounds) == ContainmentType.Disjoint)
                return ContainmentType.Disjoint;

            ContainmentType c = ContainmentType.Disjoint;

            for (int i = 0; i < Points.Length; i++)
            {
                Line edge;
                if (i == 0)
                    edge = new Line(Points[Points.Length - 1], Points[0]);
                else
                    edge = new Line(Points[i - 1], Points[i]);

                c = edge.Contains(other);
            }

            if (c != ContainmentType.Disjoint)
                return c;

            c = Contains(other.A);
            if (c == ContainmentType.Disjoint)
                return ContainmentType.Disjoint;
            c = Contains(other.B);
            if (c == ContainmentType.Disjoint)
                return ContainmentType.Disjoint;
            return ContainmentType.Contains;
        }

        public ContainmentType Contains(Polygon other)
        {
            if (Bounds.Contains(other.Bounds) == ContainmentType.Disjoint)
                return ContainmentType.Disjoint;

            ContainmentType c = other.Contains(new Line(Points[Points.Length - 1], Points[0]));
            if (c == ContainmentType.Intersects)
                return c;

            for (int i = 0; i < Points.Length; i++)
            {
                Line edge;
                if (i == 0)
                    edge = new Line(Points[Points.Length - 1], Points[0]);
                else
                    edge = new Line(Points[i - 1], Points[i]);

                if (other.Contains(edge) != c)
                    return ContainmentType.Intersects;
            }

            return c;
        }

        public ContainmentType Contains(Circle other)
        {
            if (Bounds.Contains(other.Bounds) == ContainmentType.Disjoint)
                return ContainmentType.Disjoint;

            ContainmentType c = other.Contains(new Line(Points[Points.Length - 1], Points[0]));
            if (c == ContainmentType.Intersects)
                return c;

            for (int i = 0; i < Points.Length; i++)
            {
                Line edge;
                if (i == 0)
                    edge = new Line(Points[Points.Length - 1], Points[0]);
                else
                    edge = new Line(Points[i - 1], Points[i]);

                if (other.Contains(edge) != c)
                    return ContainmentType.Intersects;
            }

            return c;
        }

        public override ContainmentType contains(Shape shape)
        {
            if (shape is PointF)
                return Contains((PointF)shape);
            if (shape is Line)
                return Contains((Line)shape);
            if (shape is Polygon)
                return Contains((Polygon)shape);
            if (shape is Circle)
                return Contains((Circle)shape);
            throw new ArgumentException("Does not support collision with type " + shape.GetType());
        }
    }
}
